package com.wgo.bpot.wiring.common;


import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Map;

import com.wgo.bpot.common.transport.exception.RematoException;
import com.wgo.bpot.common.transport.util.Assert;
import com.wgo.bpot.domain.common.BpotPersistable;

/**
 * Used to arrange for intelligent reflection.
 * 
 * {@url http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.8.2}
 * 
 * @author petterei
 */
public abstract class DomainModelConventionImpl<T> implements DomainModelConvention<T>{
	
	private Method getDomainIdentifierMethod = null ;
	private static Method getBpotIdentifierMethod = null ;
	
	private Map<Class<? extends T>,Class<? extends T>> apiToImplCache = new Hashtable<Class<? extends T>,Class<? extends T>>(10);	
	private Map<Class<? extends T>, Class<? extends T>> implToApiCache = new Hashtable<Class<? extends T>, Class<? extends T>>(10);

	
	static {
		try {
			getBpotIdentifierMethod = BpotPersistable.class.getMethod("getIdentifier");
		} catch (Exception e) {
			throw new RematoException ("Could not find method for getting identifier for persistable bpot-objects.", e) ;
		}
	}
//	private static final transient Logger log = Logger.getLogger(DomainConventionImpl.class.getName());
	
	
//	@SuppressWarnings("unchecked")
//	public static <T extends Object> Class<T> loadImplClass(Class<T> domainModelInterface){
//		String implClassName = domainModelInterface.getPackage().getName() + ".impl." + domainModelInterface.getSimpleName() + "Impl";
//		Class<T> implClass = null;
//		try {
//			implClass = (Class<T>)Class.forName(implClassName);
//		} catch (ClassNotFoundException e) {
//			throw new DatabaseException("Class not found: " + implClassName, e);
//		}
//		return implClass;
//	}
	
//	@SuppressWarnings("unchecked")
//	public static <T extends Object> Class<T> loadServerClass(Class<T> domainModelInterface)
//	{
//		String path = domainModelInterface.getPackage().getName();
//		int index = path.lastIndexOf("common");
//		String subpath = path.substring(0,index-1);
//		String implClassName = subpath  + ".server.ejb." + domainModelInterface.getSimpleName() + "Ejb";
////		log.info("  ==>> impl class name : " + implClassName);
//		Class<T> implClass = null;
//		try {
//			implClass = (Class<T>)Class.forName(implClassName);
//		} catch (ClassNotFoundException e) {
//			throw new DatabaseException("Class not found: " + implClassName + ".  domainClass=" + domainModelInterface.getName(), e);
//		}
//		return implClass;
//	}	
	
	protected DomainModelConventionImpl() {
		this("getDbId");
	}

	public DomainModelConventionImpl(String getIdentifierMethodName) {
		try {
			getDomainIdentifierMethod = getDomainModelInterface().getMethod( getIdentifierMethodName ) ;
		} catch (NoSuchMethodException e) {
			throw new RuntimeException (e);
		}
	}

//	public static Class getApiClassFromEjbClass(Class ejbClass) {
//		String domainPackageName = ejbClass.getName().substring(0,ejbClass.getName().lastIndexOf(".ejb."));
//		String apiClassName = domainPackageName + "." + ejbClass.getSimpleName().substring(0,ejbClass.getSimpleName().indexOf("Ejb"));
//		Class result = null;
//		try {
//			result = Class.forName(apiClassName,true,Thread.currentThread().getContextClassLoader());
//		} catch (ClassNotFoundException e) {
//			throw new RematoException("Class not found: " + apiClassName, e);
//		}
//		return result;
//	}

//	public static Class getApiClassFromImplClass(Class implClass) {
//		String domainPackageName = implClass.getName().substring(0,implClass.getName().lastIndexOf(".impl."));
//		String apiClassName = domainPackageName + "." + implClass.getSimpleName().substring(0,implClass.getSimpleName().lastIndexOf("Impl"));
//		Class result = null;
//		try {
//			result = Class.forName(apiClassName,true,Thread.currentThread().getContextClassLoader());
//		} catch (ClassNotFoundException e) {
//			throw new DatabaseException("Class not found: " + apiClassName, e);
//		}
//		return result;
//	}

//	public static Class getImplClassFromEjbClass(Class ejbClass) {
//		String domainPackageName = ejbClass.getName().substring(0,ejbClass.getName().lastIndexOf(".server.ejb."));
//		String apiClassName = domainPackageName + ".common.impl." + ejbClass.getSimpleName().substring(0,ejbClass.getSimpleName().lastIndexOf("Ejb")) + "Impl";
//		Class result = null;
//		try {
//			result = Class.forName(apiClassName,true,Thread.currentThread().getContextClassLoader());
//		} catch (ClassNotFoundException e) {
//			throw new RematoException("Class not found: " + apiClassName, e);
//		}
//		return result;
//	}

	/* (non-Javadoc)
	 * @see com.wgo.bpot.wiring.common.DomainModelConvention#apiToImpl(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	public Class<? extends T> apiToImpl (Class<? extends T> apiClass) {
		if (apiToImplCache.containsKey(apiClass)) {
			return apiToImplCache.get(apiClass);
		}
		String implClassName = apiClass.getPackage().getName() + ".impl." + apiClass.getSimpleName() + "Impl";
		Class<T> implClass = null;
		try {
			implClass = (Class<T>)Class.forName(implClassName);
		} catch (ClassNotFoundException e) {
			throw new RematoException("Class not found: " + implClassName, e);
		}
		apiToImplCache.put(apiClass,implClass);
		return implClass;
	}

	/* (non-Javadoc)
	 * @see com.wgo.bpot.wiring.common.DomainModelConvention#implToApi(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	public Class<? extends T> implToApi (Class<? extends T> implClass) {
		if (implToApiCache.containsKey(implClass)) {
			return implToApiCache.get(implClass);
		}
		String domainPackageName = implClass.getName().substring(0,implClass.getName().lastIndexOf(".impl."));
		String apiClassName = domainPackageName + "." + implClass.getSimpleName().substring(0,implClass.getSimpleName().lastIndexOf("Impl"));
		Class apiClass = null;
		try {
			apiClass = Class.forName(apiClassName,true,Thread.currentThread().getContextClassLoader());
		} catch (ClassNotFoundException e) {
			throw new RematoException("Class not found: " + apiClassName, e);
		}
		implToApiCache.put(implClass,apiClass);
		return apiClass;
	}

	/* (non-Javadoc)
	 * @see com.wgo.bpot.wiring.common.DomainModelConvention#getIdentifierMethod()
	 */
	public Method getIdentifierMethod(Object instance) {
		Assert.assertNotNull(instance, "instance to find method to get indentifier from cannot be 'null'.");
		Class conceptClass = instance.getClass() ;
		if (getDomainModelInterface().isAssignableFrom(conceptClass)) {
			return getDomainIdentifierMethod;
		} else if (BpotPersistable.class.isAssignableFrom(conceptClass)) {
			return getBpotIdentifierMethod ;
		}
		throw new RematoException ("Unknown type: " + conceptClass.getSimpleName() + " Cannot find method for getting identifier. ") ;
	}

	
}

